#pragma once

#ifndef DLL_VECTOR3_H
#define DLL_VECTOR3_H
#endif

/*
	mostly inspired from original Blitz3D vector class
	http://www.blitzbasic.com/Community/posts.php?topic=42657
*/
class vector3{
public:
	float x,y,z;
	
	vector3():x(0),y(0),z(0){}

	~vector3(){};

	vector3( float x,float y,float z ):x(x),y(y),z(z){}
	operator float*() {return &x;}
	operator const float *() {return &x;}
	float &operator[](int n) {return (&x)[n];}
	float operator[](int n)const {return (&x)[n];}
	vector3* operator-()const {return new vector3( -x,-y,-z );}
	vector3* operator*( float scale )const {return new vector3(x*scale,y*scale,z*scale);}
	vector3* operator*( const vector3 *q )const
		{return new vector3( x*q->x,y*q->y,z*q->z );}
	vector3* operator/( float scale )const
		{return new vector3( x/scale,y/scale,z/scale );}
	vector3* operator/( const vector3 *q )const
		{return new vector3( x/q->x,y/q->y,z/q->z );}
	vector3* operator+( const vector3 *q )const
		{return new vector3( x+q->x,y+q->y,z+q->z );}
	vector3* operator-( const vector3 *q )const
		{return new vector3( x-q->x,y-q->y,z-q->z );}
	vector3* operator*=( float f ) {x*=f;y*=f;z*=f;return this;}
	vector3* operator*=( const vector3 *q ) {x*=q->x;y*=q->y;z*=q->z;return this;}
	vector3* operator/=( float f ) {f=1.0f/f;x*=f;y*=f;z*=f;return this;}
	vector3* operator/=( const vector3 *q ) {x/=q->x;y/=q->y;z/=q->z;return this;}
	vector3* operator+=( const vector3 *q ) {x+=q->x;y+=q->y;z+=q->z;return this;}
	vector3* operator-=( const vector3 *q ) {x-=q->x;y-=q->y;z-=q->z;return this;}
	
	bool operator<( const vector3 *q )const
	{	if( fabs(x-q->x)>FLOAT_EPSILON ) return x<q->x ? true : false;
		if( fabs(y-q->y)>FLOAT_EPSILON ) return y<q->y ? true : false;
		return fabs(z-q->z)>FLOAT_EPSILON && z<q->z;}
	
	bool operator==( vector3* q )const
		{return (
				fabs(x-q->x)<=FLOAT_EPSILON &&
				fabs(y-q->y)<=FLOAT_EPSILON &&
				fabs(z-q->z)<=FLOAT_EPSILON);}
	
	bool operator!=( const vector3 *q )const
		{return (
				fabs(x-q->x)>FLOAT_EPSILON ||
				fabs(y-q->y)>FLOAT_EPSILON ||
				fabs(z-q->z)>FLOAT_EPSILON);}
	
	void Set (float X, float Y, float Z){x=X;y=Y;z=Z;};
	
	float Dot( const vector3 *q )const {return x*q->x+y*q->y+z*q->z;}
	float Angle( const vector3 *q )const {return acos(Dot(q));};
	vector3* Cross( const vector3 *q )const
		{return new vector3( y*q->z-z*q->y,z*q->x-x*q->z,x*q->y-y*q->x );}
	
	float SqLength()const {return (x*x+y*y+z*z);}
	float Length()const {return sqrtf(x*x+y*y+z*z);}
	float Distance(const vector3 &q)const
		{return sqrtf((x-q.x)*(x-q.x)+(y-q.y)*(y-q.y)+(z-q.z)*(z-q.z));}
	
	vector3* Normalized()const {float l=1.0f/Length();return new vector3( x*l,y*l,z*l );}
	void Normalize() {float l=1.0f/Length();x*=l;y*=l;z*=l;}
	
	float Yaw()const {return -atan2f(x,z);}
	float Pitch()const {return -atan2f( y,sqrtf( x*x+z*z ));}
	
	void Clear() {x=y=z=0;}
};





class vector3list;


class vector3link{
private :
	friend vector3link* NullVector3link();
	friend inline void Unlinkvector3(vector3link* lnk);

public :
	
	vector3link(){;};
	~vector3link(){Unlinkvector3(this);};
	vector3list* list;
	vector3link* next;
	vector3link* prev;
	vector3* v;
	
	int InsertAfter(vector3link* after);
	int InsertBefore(vector3link* after);
	
};

vector3link* NullVector3();


class vector3list{
public :
	
	vector3link* head;
	vector3link* tail;
	int count;

	vector3list(){		head	=	0;
						tail	=	0;
						count	=	0;
	};
	~vector3list(){		while (head!=0)
						{
							vector3link* temp(head);
							head=head->next;
							delete temp;
						};
	};
	vector3list* Copy(){
							vector3list* copy = new vector3list();
							vector3link* lnk = head;
							while (lnk!=0)
							{
								copy->AddLast(lnk->v);
								lnk=lnk->next;
							};
							return copy;
						};
	vector3link* AddLast(vector3* vector){
							vector3link* lnk=	new vector3link();
							lnk->v			=	vector;
							lnk->list		=	this;
							
							if (head==0)
							{
								head		=	lnk;
								lnk->prev	=	0;
							}
							else
							{
								tail->next	=	lnk;
								lnk->prev	=	tail;
							};
							tail			=	lnk;
							lnk->next		=	0;
							count++;
							return lnk;
	};
	vector3link* AddFirst(vector3* vector){
							vector3link* lnk=	new vector3link();
							lnk->v			=	vector;
							lnk->list		=	this;
							
							if (tail==0)
							{
								tail		=	lnk;
								lnk->next	=	0;
							}
							else
							{
								head->prev	=	lnk;
								lnk->next	=	head;
							};
							head			=	lnk;
							lnk->prev		=	0;
							count++;
							return lnk;
	};
	vector3link* Find(vector3* vector){
							if (head==0) return 0;
							vector3link* l = head;
							while (l!=0)
							{
								if (l->v==vector) return l;
								l=l->next;
							};
							return 0;
	};

};


inline void Unlinkvector3(vector3link* lnk){
	vector3list* l = lnk->list;
	
	if (lnk==l->head)
	{
		if (lnk==l->tail)
		{
			l->head=0;
			l->tail=0;
		}
		else
		{
			l->head=lnk->next;
			l->head->prev=0;
		};
	}
	else
	{
		if (lnk==l->tail)
		{
			l->tail=l->tail->prev;
			l->tail->next=0;
		}
		else
		{
			lnk->next->prev=lnk->prev;
			lnk->prev->next=lnk->next;
		};
	};
	lnk->next=0;
	lnk->prev=0;
	lnk->list->count--;
};









/* Double Linked-List for btVector3 */

class btV3Link{
	friend class btV3List;
	btV3List* _list;
	btV3Link* _next;
	btV3Link* _prev;
	btVector3* _v;
public :
	btV3Link();
	~btV3Link();
	
	inline btV3List* List(){return _list;};
	inline btV3Link* Next(){return _next;};
	inline btV3Link* Prev(){return _prev;};
	btVector3* V(){return _v;};
	
	void Set(btVector3* v){_v=v;};
	
	int InsertAfter(btV3Link*After);
	int InsertBefore(btV3Link*After);
	int Swap(btV3Link*l);
	
	friend inline void btV3Unlink(btV3Link* lnk);
};

inline void btV3Unlink(btV3Link* lnk);


class btV3List{
public :
	btV3List();
	~btV3List();
	btV3Link* _head;
	btV3Link* _tail;
	int _Count;
	inline int Count(){return _Count;};
	inline btV3Link* First(){return _head;};
	inline btV3Link* Last(){return _tail;};
	inline int IsEmpty(){return (_head==0);};
	btV3List*Copy();
	btV3Link*AddLast(btVector3* pValue);
	btV3Link*AddFirst(btVector3* pValue);
	btV3Link*Find(btVector3* pV);
};
